<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>大数据实时处理系统 - 数据可视化大屏</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@3"></script>
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
            background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
            color: #e2e8f0;
            min-height: 100vh;
            padding: 20px;
        }
        
        .dashboard {
            max-width: 1400px;
            margin: 0 auto;
            display: grid;
            grid-template-columns: 2fr 1fr;
            grid-template-rows: auto auto 1fr;
            gap: 20px;
            height: calc(100vh - 40px);
        }
        
        .header {
            grid-column: 1 / -1;
            text-align: center;
            padding: 20px;
            background: rgba(30, 41, 59, 0.8);
            border-radius: 12px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
            backdrop-filter: blur(10px);
        }
        
        .header h1 {
            font-size: 2.5rem;
            font-weight: 700;
            background: linear-gradient(45deg, #60a5fa, #38bdf8);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            margin-bottom: 10px;
        }
        
        .header p {
            color: #94a3b8;
            font-size: 1.1rem;
        }
        
        .stats-grid {
            grid-column: 1 / -1;
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
            gap: 20px;
        }
        
        .stat-card {
            background: rgba(30, 41, 59, 0.8);
            border-radius: 12px;
            padding: 24px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
            backdrop-filter: blur(10px);
            transition: transform 0.3s ease, box-shadow 0.3s ease;
        }
        
        .stat-card:hover {
            transform: translateY(-5px);
            box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
        }
        
        .stat-number {
            font-size: 2.8rem;
            font-weight: 700;
            margin-bottom: 8px;
            background: linear-gradient(45deg, #4ade80, #22c55e);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
        }
        
        .stat-label {
            color: #94a3b8;
            font-size: 1rem;
            font-weight: 500;
        }
        
        .chart-container {
            background: rgba(30, 41, 59, 0.8);
            border-radius: 12px;
            padding: 20px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
            backdrop-filter: blur(10px);
            position: relative;
        }
        
        .chart-title {
            font-size: 1.2rem;
            font-weight: 600;
            margin-bottom: 16px;
            color: #e2e8f0;
            text-align: center;
        }
        
        .chart {
            width: 100%;
            height: calc(100% - 50px);
        }
        
        .messages-container {
            background: rgba(30, 41, 59, 0.8);
            border-radius: 12px;
            padding: 20px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
            backdrop-filter: blur(10px);
            overflow: hidden;
            display: flex;
            flex-direction: column;
        }
        
        .messages-title {
            font-size: 1.2rem;
            font-weight: 600;
            margin-bottom: 16px;
            color: #e2e8f0;
        }
        
        .messages-list {
            flex: 1;
            overflow-y: auto;
            margin-bottom: 16px;
        }
        
        .message-item {
            background: rgba(51, 65, 85, 0.6);
            border-radius: 8px;
            padding: 12px;
            margin-bottom: 8px;
            animation: fadeIn 0.3s ease;
        }
        
        .message-time {
            font-size: 0.8rem;
            color: #94a3b8;
            margin-bottom: 4px;
        }
        
        .message-content {
            color: #e2e8f0;
            word-break: break-word;
        }
        
        .controls {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 16px 20px;
            background: rgba(30, 41, 59, 0.8);
            border-radius: 12px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
            backdrop-filter: blur(10px);
        }
        
        .refresh-info {
            color: #94a3b8;
            font-size: 0.9rem;
        }
        
        .refresh-btn {
            background: linear-gradient(45deg, #60a5fa, #38bdf8);
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 8px;
            font-size: 0.9rem;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        
        .refresh-btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(96, 165, 250, 0.4);
        }
        
        .refresh-btn:active {
            transform: translateY(0);
        }
        
        @keyframes fadeIn {
            from {
                opacity: 0;
                transform: translateY(10px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }
        
        /* 滚动条样式 */
        .messages-list::-webkit-scrollbar {
            width: 6px;
        }
        
        .messages-list::-webkit-scrollbar-track {
            background: rgba(51, 65, 85, 0.3);
            border-radius: 3px;
        }
        
        .messages-list::-webkit-scrollbar-thumb {
            background: rgba(96, 165, 250, 0.5);
            border-radius: 3px;
        }
        
        .messages-list::-webkit-scrollbar-thumb:hover {
            background: rgba(96, 165, 250, 0.8);
        }
        
        /* 响应式设计 */
        @media (max-width: 768px) {
            .dashboard {
                grid-template-columns: 1fr;
                height: auto;
            }
            
            .header h1 {
                font-size: 1.8rem;
            }
            
            .stat-number {
                font-size: 2rem;
            }
            
            .chart {
                height: 300px;
            }
        }
    </style>
</head>
<body>
    <div id="app">
        <div class="dashboard">
            <div class="header">
                <h1>大数据实时处理系统</h1>
                <p>基于Kafka + Spring Boot + Vue.js的数据可视化大屏</p>
            </div>
            
            <div class="stats-grid">
                <div class="stat-card">
                    <div class="stat-number">{{ stats.totalMessages }}</div>
                    <div class="stat-label">总消息数</div>
                </div>
                <div class="stat-card">
                    <div class="stat-number">{{ stats.messagesPerMinute }}</div>
                    <div class="stat-label">每分钟消息量</div>
                </div>
                <div class="stat-card">
                    <div class="stat-number">{{ stats.uniqueWords }}</div>
                    <div class="stat-label">唯一单词数</div>
                </div>
                <div class="stat-card">
                    <div class="stat-number">{{ stats.topWords.length }}</div>
                    <div class="stat-label">热门词数</div>
                </div>
            </div>
            
            <div class="chart-container">
                <div class="chart-title">词频统计（TOP 20）</div>
                <div id="wordCountChart" class="chart"></div>
            </div>
            
            <div class="chart-container">
                <div class="chart-title">词频分布</div>
                <div id="wordPieChart" class="chart"></div>
            </div>
            
            <div class="messages-container">
                <div class="messages-title">最近消息</div>
                <div class="messages-list">
                    <div v-for="message in recentMessages" :key="message.id" class="message-item">
                        <div class="message-time">{{ formatTime(message.timestamp) }}</div>
                        <div class="message-content">{{ message.content }}</div>
                    </div>
                </div>
            </div>
        </div>
        
        <div class="controls">
            <div class="refresh-info">
                上次更新时间: {{ lastUpdateTime }}
            </div>
            <button class="refresh-btn" @click="refreshData">手动刷新</button>
        </div>
    </div>
    
    <script>
        const { createApp, ref, onMounted, computed } = Vue;
        
        const app = createApp({
            setup() {
                // 响应式数据
                const stats = ref({
                    totalMessages: 0,
                    messagesPerMinute: 0,
                    uniqueWords: 0,
                    topWords: []
                });
                
                const wordCounts = ref({});
                const recentMessages = ref([]);
                const lastUpdateTime = ref('');
                const refreshInterval = ref(5000); // 5秒自动刷新
                const timer = ref(null);
                
                // 初始化图表
                const initCharts = () => {
                    // 词频柱状图
                    window.wordCountChart = echarts.init(document.getElementById('wordCountChart'));
                    // 词频饼图
                    window.wordPieChart = echarts.init(document.getElementById('wordPieChart'));
                    
                    // 设置初始选项
                    const initialBarOption = {
                        tooltip: {
                            trigger: 'axis',
                            axisPointer: {
                                type: 'shadow'
                            },
                            backgroundColor: 'rgba(30, 41, 59, 0.9)',
                            borderColor: '#475569',
                            textStyle: {
                                color: '#e2e8f0'
                            }
                        },
                        grid: {
                            left: '3%',
                            right: '4%',
                            bottom: '3%',
                            containLabel: true
                        },
                        xAxis: {
                            type: 'value',
                            axisLabel: {
                                color: '#94a3b8'
                            },
                            axisLine: {
                                lineStyle: {
                                    color: '#475569'
                                }
                            },
                            splitLine: {
                                lineStyle: {
                                    color: '#334155'
                                }
                            }
                        },
                        yAxis: {
                            type: 'category',
                            data: [],
                            axisLabel: {
                                color: '#94a3b8'
                            },
                            axisLine: {
                                lineStyle: {
                                    color: '#475569'
                                }
                            }
                        },
                        series: [{
                            name: '词频',
                            type: 'bar',
                            data: [],
                            itemStyle: {
                                color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
                                    { offset: 0, color: '#60a5fa' },
                                    { offset: 1, color: '#38bdf8' }
                                ])
                            }
                        }]
                    };
                    
                    const initialPieOption = {
                        tooltip: {
                            trigger: 'item',
                            backgroundColor: 'rgba(30, 41, 59, 0.9)',
                            borderColor: '#475569',
                            textStyle: {
                                color: '#e2e8f0'
                            }
                        },
                        legend: {
                            orient: 'vertical',
                            right: 10,
                            top: 'center',
                            textStyle: {
                                color: '#94a3b8'
                            }
                        },
                        series: [{
                            name: '词频分布',
                            type: 'pie',
                            radius: ['40%', '70%'],
                            center: ['40%', '50%'],
                            avoidLabelOverlap: false,
                            itemStyle: {
                                borderRadius: 8,
                                borderColor: '#1e293b',
                                borderWidth: 2
                            },
                            label: {
                                show: false,
                                position: 'center'
                            },
                            emphasis: {
                                label: {
                                    show: true,
                                    fontSize: '16',
                                    fontWeight: 'bold',
                                    color: '#e2e8f0'
                                }
                            },
                            labelLine: {
                                show: false
                            },
                            data: []
                        }]
                    };
                    
                    if (window.wordCountChart) {
                        window.wordCountChart.setOption(initialBarOption);
                    }
                    if (window.wordPieChart) {
                        window.wordPieChart.setOption(initialPieOption);
                    }
                };
                
                // 更新图表数据
                const updateCharts = () => {
                    // 准备词频数据，取前20个
                    const sortedWords = Object.entries(wordCounts.value)
                        .sort((a, b) => b[1] - a[1])
                        .slice(0, 20);
                    
                    // 更新柱状图
                    const barOption = {
                        yAxis: {
                            data: sortedWords.map(([word]) => word).reverse()
                        },
                        series: [{
                            data: sortedWords.map(([, count]) => count).reverse()
                        }]
                    };
                    
                    if (window.wordCountChart) {
                        window.wordCountChart.setOption(barOption);
                    }
                    
                    // 更新饼图（取前8个）
                    const pieData = sortedWords.slice(0, 8).map(([word, count]) => ({
                        name: word,
                        value: count
                    }));
                    
                    // 如果有更多词汇，添加"其他"类别
                    const total = Object.values(wordCounts.value).reduce((sum, count) => sum + count, 0);
                    const displayedTotal = pieData.reduce((sum, item) => sum + item.value, 0);
                    
                    if (displayedTotal < total) {
                        pieData.push({
                            name: '其他',
                            value: total - displayedTotal
                        });
                    }
                    
                    // 饼图颜色
                    const pieColors = [
                        '#60a5fa', '#38bdf8', '#4ade80', '#22c55e',
                        '#fb923c', '#f97316', '#f472b6', '#ec4899',
                        '#a78bfa', '#8b5cf6'
                    ];
                    
                    const pieOption = {
                        series: [{
                            data: pieData,
                            itemStyle: {
                                color: (params) => pieColors[params.dataIndex % pieColors.length]
                            }
                        }]
                    };
                    
                    if (window.wordPieChart) {
                        window.wordPieChart.setOption(pieOption);
                    }
                };
                
                // 格式化时间
                const formatTime = (timestamp) => {
                    const date = new Date(timestamp);
                    return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date.getSeconds().toString().padStart(2, '0')}`;
                };
                
                // 刷新数据
                const refreshData = async () => {
                    try {
                        // 获取统计数据
                        const statsResponse = await fetch('/api/visualization/stats');
                        const statsData = await statsResponse.json();
                        stats.value = statsData;
                        
                        // 获取词频数据
                        const wordCountsResponse = await fetch('/api/visualization/wordCounts');
                        const wordCountsData = await wordCountsResponse.json();
                        wordCounts.value = wordCountsData;
                        
                        // 获取最近消息
                        const messagesResponse = await fetch('/api/visualization/recentMessages');
                        const messagesData = await messagesResponse.json();
                        recentMessages.value = messagesData;
                        
                        // 更新图表
                        updateCharts();
                        
                        // 更新最后更新时间
                        lastUpdateTime.value = formatTime(new Date());
                    } catch (error) {
                        console.error('刷新数据失败:', error);
                    }
                };
                
                // 开始定时器
                const startTimer = () => {
                    timer.value = setInterval(() => {
                        refreshData();
                    }, refreshInterval.value);
                };
                
                // 重置定时器
                const resetTimer = () => {
                    if (timer.value) {
                        clearInterval(timer.value);
                    }
                    startTimer();
                };
                
                // 组件挂载时执行
                onMounted(() => {
                    initCharts();
                    refreshData();
                    startTimer();
                });
                
                return {
                    stats,
                    wordCounts,
                    recentMessages,
                    lastUpdateTime,
                    formatTime,
                    refreshData
                };
            }
        });
        
        app.mount('#app');
        
        // 窗口大小改变时重新调整图表大小
        window.addEventListener('resize', () => {
            if (window.wordCountChart) {
                window.wordCountChart.resize();
            }
            if (window.wordPieChart) {
                window.wordPieChart.resize();
            }
        });
    </script>
</body>
</html>